home *** CD-ROM | disk | FTP | other *** search
/ X User Tools / X User Tools (O'Reilly and Associates)(1994).ISO / sources / xman / man.c < prev    next >
C/C++ Source or Header  |  1994-09-27  |  22KB  |  899 lines

  1. /*
  2.  * xman - X Window System manual page display program.
  3.  *
  4.  * $XConsortium: man.c,v 1.29 91/07/30 22:03:20 rws Exp $
  5.  *
  6.  * Copyright 1987, 1988 Massachusetts Institute of Technology
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies and that both that
  11.  * copyright notice and this permission notice appear in supporting
  12.  * documentation, and that the name of M.I.T. not be used in advertising or
  13.  * publicity pertaining to distribution of the software without specific,
  14.  * written prior permission.  M.I.T. makes no representations about the
  15.  * suitability of this software for any purpose.  It is provided "as is"
  16.  * without express or implied warranty.
  17.  *
  18.  * Author:    Chris D. Peterson, MIT Project Athena
  19.  * Created:   August 10, 1987
  20.  */
  21.  
  22. #include "globals.h"
  23. #include "vendor.h"        /* vendor-specific defines and data */
  24.  
  25. #ifndef X_NOT_POSIX
  26. #include <dirent.h>
  27. #else
  28. #ifdef SYSV
  29. #include <dirent.h>
  30. #else
  31. #ifdef USG
  32. #include <dirent.h>
  33. #else
  34. #include <sys/dir.h>
  35. #ifndef dirent
  36. #define dirent direct
  37. #endif
  38. #endif
  39. #endif
  40. #endif
  41.  
  42. #ifdef DEBUG
  43. static char error_buf[BUFSIZ];        /* The buffer for error messages. */
  44. #endif /* DEBUG */
  45.  
  46. static void SortList(), ReadMandescFile(), SortAndRemove(), InitManual();
  47. static void AddToCurrentSection();
  48. static void ReadCurrentSection();
  49.  
  50. #define SECT_ERROR -1
  51.  
  52. /*    Function Name: Man
  53.  *    Description: Builds a list of all manual directories and files.
  54.  *    Arguments: none. 
  55.  *    Returns: the number of manual sections.
  56.  */
  57.  
  58. int
  59. Man()
  60. {
  61.   SectionList *list = NULL;
  62.   char *ptr, manpath[BUFSIZ], *path, *current_label;
  63.   int sect, num_alloced;
  64.  
  65. /* 
  66.  * Get the environment variable MANPATH, and if it doesn't exist then use
  67.  * SYSMANPATH and LOCALMANPATH.
  68.  */
  69.  
  70.   ptr = getenv("MANPATH");
  71.   if (ptr == NULL || streq(ptr , "") ) {
  72.     strcpy(manpath, SYSMANPATH);
  73. #ifdef LOCALMANPATH
  74.     strcat(manpath, ":");
  75.     strcat(manpath, LOCALMANPATH);
  76. #endif
  77.   } else {
  78.     strcpy(manpath, ptr);
  79.   }
  80.  
  81. /*
  82.  * Get the list of manual directories in the users MANPATH that we should
  83.  * open to look for manual pages.  The ``mandesc'' file is read here.
  84.  */
  85.  
  86.   for ( path = manpath ; (ptr = index(path , ':')) != NULL ; path = ++ptr) { 
  87.     *ptr = '\0';
  88.     ReadMandescFile(&list, path);
  89.   }
  90.   ReadMandescFile(&list, path);
  91.  
  92.   SortList(&list);
  93.   
  94.   sect = 0;
  95.   num_alloced = SECTALLOC;
  96.   manual = (Manual *) XtMalloc( sizeof(Manual) * num_alloced );
  97.   InitManual( manual, list->label );
  98.   manual[sect].flags = list->flags;
  99.   current_label = NULL;
  100.  
  101.   while ( list != NULL ) {
  102.     SectionList * old_list;
  103.  
  104.     if ( current_label == NULL || streq(list->label, current_label) )
  105.       AddToCurrentSection( manual + sect, list->directory);
  106.     else {
  107.       if (manual[sect].nentries == 0) {    /* empty section, re-use it. */
  108.     XtFree(manual[sect].blabel);
  109.     manual[sect].blabel = list->label;
  110.     manual[sect].flags = list->flags;
  111.       }
  112.       else {
  113.     if ( ++sect >= num_alloced ) {
  114.       num_alloced += SECTALLOC;
  115.       manual = (Manual *) realloc ( (char *) manual,
  116.                         (sizeof(Manual) * num_alloced));
  117.       if (manual == NULL) 
  118.         PrintError("Could not allocate memory for manual sections.");
  119.     }
  120.     InitManual( manual + sect, list->label );
  121.     manual[sect].flags = list->flags;
  122.       }
  123.       AddToCurrentSection( manual + sect, list->directory);
  124.     }
  125.     /* Save label to see if it matches next entry. */
  126.     current_label = list->label; 
  127.     old_list = list;
  128.     list = list->next;
  129.     XtFree((char *) old_list);        /* free what you allocate. */
  130.   }
  131.   if (manual[sect].nentries != 0)
  132.     sect++;            /* don't forget that last section. */
  133.   
  134.   SortAndRemove(manual, sect);
  135.  
  136. #ifdef notdef            /* dump info. */
  137.   DumpManual(sect);
  138. #endif
  139.   
  140. /*
  141.  * realloc manual to be minimum space necessary.
  142.  */
  143.  
  144.   manual = (Manual *) realloc( (char *) manual, (sizeof(Manual) * sect));
  145.   if (manual == NULL) 
  146.     PrintError("Could not allocate memory for manual sections.");
  147.  
  148.   return(sect);        /* return the number of man sections. */
  149. }    
  150.  
  151. /*    Function Name: SortList
  152.  *    Description: Sorts the list of sections to search.
  153.  *    Arguments: list - a pointer to the list to sort.
  154.  *    Returns: a sorted list.
  155.  *
  156.  * This is the most complicated part of the entire operation.
  157.  * all sections with the same label must by right next to each other,
  158.  * but the sections that are in the standard list have to come first.
  159.  */
  160.  
  161. static void
  162. SortList(list)
  163. SectionList ** list;
  164. {
  165.   SectionList * local;
  166.   SectionList *head, *last, *inner, *old;
  167.   
  168.   if (*list == NULL)
  169.     PrintError("No manual sections to read, exiting.");
  170.  
  171. /* 
  172.  * First step 
  173.  * 
  174.  * Look for standard list items, and more them to the top of the list.
  175.  */
  176.  
  177.   last = NULL;            /* keep Saber happy. */
  178.   for ( local = *list ; local->next != NULL ; local = local->next) {
  179.     if ( local->flags ) {
  180.       if ( local == *list )    /* top element is already standard. */
  181.     break;
  182.       head = local;
  183.  
  184.       /* Find end of standard block */
  185.       for ( ; (local->next != NULL) && (local->flags) 
  186.        ; old = local, local = local->next); 
  187.  
  188.       last->next = old->next; /* Move the block. */
  189.       old->next = *list;
  190.       *list = head;
  191.  
  192.       break;            /* First step accomplished. */
  193.     }
  194.     last = local;
  195.   }
  196.  
  197. /*
  198.  *  Second step
  199.  *
  200.  *  Move items with duplicate labels right next to each other.
  201.  */
  202.  
  203.   local = *list;
  204.   for ( local = *list ; local->next != NULL ; local = local->next) {
  205.     inner = local->next;
  206.     while ( inner != NULL) {
  207.       if ( streq(inner->label, local->label) && (inner != local->next)) {
  208.     last->next = inner->next; /* Move it to directly follow local. */
  209.     inner->next = local->next;
  210.     local->next = inner;
  211.     inner = last;        /* just so that we keep marching down the
  212.                    tree (this keeps us from looping). */
  213.       }
  214.       last = inner;
  215.       inner = inner->next;
  216.     }
  217.   }
  218. }    
  219.  
  220. /*    Function Name: ReadMandescFile
  221.  *    Description: Reads the mandesc file, and adds more sections as 
  222.  *                   nescessary.
  223.  *    Arguments: path - path name if the current search directory.
  224.  *                 section_list - pointer to the list of sections.
  225.  *    Returns: TRUE in we should use default sections
  226.  */
  227.   
  228. static void
  229. ReadMandescFile( section_list, path )
  230. SectionList ** section_list;
  231. char * path;
  232. {
  233.   char mandesc_file[BUFSIZ];    /* full path to the mandesc file. */
  234.   FILE * descfile;
  235.   char string[BUFSIZ], local_file[BUFSIZ];
  236.   Boolean use_defaults = TRUE;
  237.   char *cp;
  238.  
  239.   sprintf(mandesc_file, "%s/%s", path, MANDESC);
  240.   if ( (descfile = fopen(mandesc_file, "r")) != NULL) {
  241.     while ( fgets(string, BUFSIZ, descfile) != NULL) {
  242.       string[strlen(string)-1] = '\0';        /* Strip off the CR. */
  243.  
  244.       if ( streq(string, NO_SECTION_DEFAULTS) ) {
  245.     use_defaults = FALSE;
  246.     continue;
  247.       }
  248.  
  249.       if ((cp = index(string,'\t')) != NULL) {
  250.     char *s;
  251.     *cp++ = '\0';
  252.     strcpy(local_file, MAN);
  253.     strcat(local_file, string);
  254.     if ((s = index(cp,'\t')) != NULL) {
  255.       *s++ = '\0';
  256.       if (streq(s, SUFFIX))
  257.         AddNewSection(section_list, path, local_file, cp, MSUFFIX);
  258.       else if (streq(s, FOLD))
  259.         AddNewSection(section_list, path, local_file, cp, MFOLD);
  260.       else if (streq(s, FOLDSUFFIX))
  261.         AddNewSection(section_list, path, local_file, cp, MFOLDSUFFIX);
  262.       else
  263.         AddNewSection(section_list, path, local_file, cp, MNULL);
  264.         } else
  265.         AddNewSection(section_list, path, local_file, cp, MNULL);
  266.       } else {
  267.     sprintf(local_file, "%s%c", MAN, string[0]);
  268.     AddNewSection(section_list, path, local_file, (string + 1), FALSE );
  269.       }
  270.     }
  271.  
  272.     fclose(descfile);
  273.   }
  274.   if (use_defaults)
  275.     AddStandardSections(section_list, path);
  276. }
  277.  
  278. /*    Function Name: AddNewSection
  279.  *    Description: Adds the new section onto the current section list.
  280.  *    Arguments: list - pointer to the section list.
  281.  *                 path - the path to the current manual section.
  282.  *                 file - the file to save.
  283.  *                 label - the current section label.
  284.  *                 flags = 1 - add a suffix
  285.  *             = 2 - fold to lower case
  286.  *    Returns: none.
  287.  */
  288.  
  289. void
  290. AddNewSection(list, path, file, label, flags)
  291. SectionList **list;
  292. char * path, * label, * file;
  293. int flags;
  294. {
  295.   SectionList * local_list, * end;
  296.   char full_path[BUFSIZ];
  297.  
  298. /* Allocate a new list element */
  299.  
  300.   local_list = (SectionList *) XtMalloc(sizeof(SectionList));
  301.  
  302.   if (*list != NULL) {
  303.     for ( end = *list ; end->next != NULL ; end = end->next );
  304.     end->next = local_list;
  305.   }
  306.   else 
  307.     *list = local_list;
  308.  
  309.   local_list->next = NULL;
  310.   local_list->label = StrAlloc(label);
  311.   sprintf(full_path, "%s/%s", path, file);
  312.   local_list->directory = StrAlloc(full_path);
  313.   local_list->flags = flags;
  314. }  
  315.  
  316. /*    Function Name: AddToCurrentSection
  317.  *    Description: This function gets the names of the manual page
  318.  *                   directories, then closes the directory.
  319.  *    Arguments:  local_manual - a pointer to a manual pages structure.
  320.  *                  path - the path to this directory.
  321.  *    Returns: none.
  322.  */
  323.  
  324. static void
  325. AddToCurrentSection(local_manual, path)
  326. Manual * local_manual;
  327. char * path;
  328. {
  329.   char temp_path[BUFSIZ];
  330.  
  331.   ReadCurrentSection(local_manual, path);
  332.   sprintf(temp_path, "%s.%s", path, COMPRESSION_EXTENSION);
  333.   ReadCurrentSection(local_manual, temp_path);
  334. }
  335.  
  336. /*    Function Name: ReadCurrentSection
  337.  *    Description: Actually does the work of adding entries to the 
  338.  *                   new section
  339.  *    Arguments:  local_manual - a pointer to a manual pages structure.
  340.  *                  path - the path to this directory.
  341.  *                  compressed - Is this a compressed directory?
  342.  *    Returns: TRUE if any entries are found.
  343.  */
  344.  
  345. static void
  346. ReadCurrentSection(local_manual, path)
  347. Manual * local_manual;
  348. char * path;
  349. {
  350.   DIR * dir;
  351.  
  352.   register struct dirent *dp;
  353.  
  354.   register int nentries;
  355.   register int nalloc;
  356.   char full_name[BUFSIZ], *ptr;
  357.  
  358.   if((dir = opendir(path)) == NULL) {    
  359. #ifdef DEBUG
  360.     sprintf(error_buf,"Can't open directory %s", path);
  361.     PopupWarning(NULL, error_buf);
  362. #endif /* DEBUG */
  363.     return;
  364.   }
  365.  
  366. /*
  367.  * Remove the compression extension from the path name.
  368.  */
  369.  
  370.   if ( (ptr = rindex(path, '.')) != NULL) 
  371.     if (streq(ptr + 1, COMPRESSION_EXTENSION)) 
  372.       *ptr = '\0';
  373.   
  374.   nentries = local_manual->nentries;
  375.   nalloc = local_manual->nalloc;
  376.  
  377.   while( (dp = readdir(dir)) != NULL ) {
  378.     char * name = dp->d_name;
  379.     if (name[0] == '.')
  380.       continue;
  381. #ifndef CRAY
  382.     if (index(name, '.') == NULL)
  383.       continue;
  384. #endif
  385.     if( nentries >= nalloc ) {
  386.       nalloc += ENTRYALLOC;
  387.       local_manual->entries =(char **) XtRealloc((char *)local_manual->entries,
  388.                          nalloc * sizeof(char *));
  389.       local_manual->entries_less_paths =
  390.     (char **) XtRealloc((char *)local_manual->entries_less_paths,
  391.                 nalloc * sizeof(char *));
  392.     }
  393.  
  394.     sprintf(full_name, "%s/%s", path, name);
  395. /*
  396.  * Remove the compression extension from the entry name.
  397.  */
  398.  
  399.     if ( (ptr = rindex(full_name, '.')) != NULL) 
  400.       if (streq(ptr + 1, COMPRESSION_EXTENSION)) 
  401.     *ptr = '\0';
  402.     local_manual->entries[nentries] = StrAlloc(full_name);
  403.     local_manual->entries_less_paths[nentries] = 
  404.       rindex(local_manual->entries[nentries], '/');
  405.     if ( local_manual->entries_less_paths[nentries] == NULL )
  406.       PrintError("Internal error while cataloging manual pages.");
  407.     ++ nentries;
  408.   }
  409.   
  410.   local_manual->nentries = nentries;
  411.   local_manual->nalloc = nalloc;
  412.  
  413.   closedir(dir);
  414. }
  415.  
  416. /*    Function Name: SortAndRemove
  417.  *    Description: This function sorts all the entry names and
  418.  *                   then removes all the duplicate entries.
  419.  *    Arguments: man - a pointer to the manual structure.
  420.  *                 number - the number of manual sections.
  421.  *    Returns: an improved manual stucure
  422.  */
  423.  
  424. static void
  425. SortAndRemove(man, number)
  426. Manual *man;
  427. int number;
  428. {
  429.   int i,j;
  430.   char *l1, *l2, **s1;
  431.   
  432.   for ( i = 0; i < number; man++, i++) { /* sort each section */
  433.     register int j = 0;      
  434.     
  435. #ifdef DEBUG
  436.     printf("sorting section %d - %s\n", i, man->blabel);
  437. #endif /* DEBUG */
  438.  
  439.     s1 = (char **)malloc(man->nentries * sizeof(char *));
  440.     
  441.     /* temporarily remove suffixes of entries, preventing them from */
  442.     /* being used in alpabetic comparison ie sccs-delta.1 vs sccs.1 */
  443.     for (j=0; j<man->nentries; j++)
  444.       if ((s1[j] = rindex(man->entries_less_paths[j], '.')) != NULL)
  445.     *s1[j] = '\0';  
  446.  
  447.     sortstrs ( man->entries_less_paths, man->nentries, man->entries );
  448.  
  449.     /* put back suffixes */
  450.     for (j=0; j<man->nentries; j++) 
  451.       if (s1[j] != NULL) *s1[j] = '.';      
  452.  
  453.     free(s1); 
  454.     
  455. #ifdef DEBUG
  456.     printf("removing from section %d.\n", i);
  457. #endif /* DEBUG */
  458.     
  459.     {
  460.       register int   j, k, nent, nentm1;
  461.       int     j2;
  462.       nent   = man -> nentries;
  463.       nentm1 = nent - 1;
  464.       j = 0;
  465.       l2 = man->entries_less_paths[j++];
  466.       if ( l2 == NULL )
  467.         PrintError("Internal error while removing duplicate manual pages.");
  468.       while ( j < nentm1 )
  469.     {
  470.       l1 = l2;
  471.       l2 = man->entries_less_paths[j++];
  472.       if ( l2 == NULL )
  473.         PrintError("Internal error while removing duplicate manual pages."
  474.                );
  475.       if ( streq(l1,l2) )
  476.         {
  477.           j2 = j-1;
  478.           k  = j2;
  479.           while ( j < nent )
  480.                 {
  481.           man -> entries_less_paths[k] = man -> entries_less_paths[j];
  482.                 man -> entries[k++] = man -> entries[j++];
  483.                 }
  484.           j = j2;
  485.           -- man -> nentries;
  486.           -- nent;
  487.           -- nentm1;
  488.         }
  489.     }
  490.     }
  491.   }
  492. }
  493.  
  494.  /*
  495.        *******  Replacement for qsort to keep
  496.        *******  identical entries in order
  497.  
  498.        A somewhat ugly hack of something that was once simpler...
  499.  */
  500.  /*
  501.        Sort an array of pointers to strings, keeping it
  502.        in ascending order by (1) string comparison and
  503.        (2) original entry order in the pointer array.
  504.  
  505.        This is a modified radix exchange algorithm.
  506.  
  507.        In case there's insufficient memory for a temporary copy
  508.        of the pointer array, the original order of identical strings
  509.        isn't preserved.
  510.  */
  511.  
  512. #ifndef       Byte
  513. #define       Byte    unsigned char
  514. #endif
  515.  
  516. #ifndef       reg
  517. #define       reg     register
  518. #endif
  519.  
  520.  
  521.  
  522.  sortstrs ( data, size, otherdata )    /*  Sort an array of string ptrs  */
  523.  
  524.        Byte    *data[];
  525.        int      size;
  526.        Byte    *otherdata[];
  527.  
  528.  {
  529.        Byte   **sp, **ep;
  530.        Byte   **othersp, **otherep;
  531.        int     *origorder;
  532.  
  533.  origorder = (int *) calloc (size, sizeof(int));
  534.  if ( origorder )
  535.     {
  536.     reg int     i;
  537.  
  538.     for ( i=0; i < size; ++i )
  539.        origorder[i] = i;
  540.     }
  541.  
  542.  sp = data;
  543.  ep = &data[size-1];
  544.  othersp = otherdata;
  545.  otherep = &otherdata[size-1];
  546.  if ( origorder )
  547.     {
  548.     sortstrs_block_oo ( sp, ep, 0, 0x80, origorder, &origorder[size-1],
  549.        othersp, otherep );
  550.     free (origorder);
  551.     }
  552.  else
  553.     sortstrs_block ( sp, ep, 0, 0x80, othersp, otherep );
  554.  }
  555.  
  556.  
  557.  
  558.  /*---------------------------------*/
  559.  /*  Sort 1 block of data on 1 bit  */
  560.  /*---------------------------------*/
  561.  
  562.  sortstrs_block ( start, end, offset, mask, otherstart, otherend )
  563.  
  564.        Byte   **start;
  565.        Byte   **end;
  566.        int      offset;
  567.        Byte     mask;
  568.        Byte   **otherstart;
  569.        Byte   **otherend;
  570.  
  571.  {
  572.  reg   Byte   **sp, **ep;
  573.  reg   Byte     m;
  574.  reg   int      off;
  575.  reg   Byte    *t;
  576.  reg   int      curstrlen;
  577.        int      maxstrlen;
  578.        Byte   **othersp, **otherep;
  579.  
  580.  
  581. #define       newstring(ptr) \
  582.  { \
  583.  t = *ptr; \
  584.  curstrlen = 0; \
  585.  while ( *t++ ) ++ curstrlen; \
  586.  if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
  587.  t = *ptr; \
  588.  }
  589.  
  590.  
  591.  maxstrlen = 0;
  592.  sp  = start;
  593.  ep  = end;
  594.  off = offset;
  595.  m   = mask;
  596.  othersp = otherstart;
  597.  otherep = otherend;
  598.  
  599.  while (1)
  600.      {
  601.      newstring(sp)
  602.      while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
  603.        {
  604.        ++ sp;
  605.        ++ othersp;
  606.        newstring(sp)
  607.        }
  608.      if ( sp == ep )
  609.        break;
  610.  
  611.      newstring(ep);
  612.      while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
  613.        {
  614.        -- ep;
  615.        -- otherep;
  616.        newstring(ep)
  617.        }
  618.      if ( sp == ep )
  619.        break;
  620.  
  621.      t = *sp;
  622.      *sp = *ep;
  623.      *ep = t;
  624.  
  625.      t      = *othersp;
  626.      *othersp = *otherep;
  627.      *otherep = t;
  628.      }
  629.  
  630.  t = *sp;
  631.  if ((curstrlen < off) || ((t[off] & m) == 0))
  632.     {
  633.     if ( ep != end )
  634.        {
  635.        ++ ep;
  636.        ++ otherep;
  637.        }
  638.     }
  639.  else
  640.     {
  641.     if ( sp != start )
  642.        {
  643.        -- sp;
  644.        -- othersp;
  645.        }
  646.     }
  647.  
  648.  m >>= 1;
  649.  if ( m == 0 )
  650.     {
  651.     m = 0x80;
  652.     if ( ++off >= maxstrlen )
  653.        return;
  654.     }
  655.  
  656.  
  657.  if ( sp != start )
  658.     sortstrs_block ( start, sp, off, m, otherstart, othersp );
  659.  if ( ep != end )
  660.     sortstrs_block ( ep, end, off, m, otherep, otherend );
  661.  }
  662.  
  663.  
  664.  
  665.  /*-----------------------------------------------------------------*/
  666.  /*  Sort 1 block of data on 1 bit; check for out-of-order entries  */
  667.  /*-----------------------------------------------------------------*/
  668.  
  669.  sortstrs_block_oo ( start, end, offset, mask, ostart, oend,
  670.                         otherstart, otherend )
  671.  
  672.        Byte   **start;
  673.        Byte   **end;
  674.        int      offset;
  675.        Byte     mask;
  676.        int     *ostart;
  677.        int     *oend;
  678.        Byte   **otherstart;
  679.        Byte   **otherend;
  680.  
  681.  {
  682.  reg   Byte   **sp, **ep;
  683.  reg   int     *osp, *oep;
  684.  reg   Byte     m;
  685.  reg   int      off;
  686.  reg   Byte    *t;
  687.  reg   int      u;
  688.  reg   int      curstrlen;
  689.        int      maxstrlen;
  690.        Byte   **othersp, **otherep;
  691.  
  692.  
  693. #define       newstring(ptr) \
  694.  { \
  695.  t = *ptr; \
  696.  curstrlen = 0; \
  697.  while ( *t++ ) ++ curstrlen; \
  698.  if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
  699.  t = *ptr; \
  700.  }
  701.  
  702.  
  703.  maxstrlen = 0;
  704.  sp  = start;
  705.  ep  = end;
  706.  osp = ostart;
  707.  oep = oend;
  708.  off = offset;
  709.  m   = mask;
  710.  othersp = otherstart;
  711.  otherep = otherend;
  712.  
  713.  while (1)
  714.      {
  715.      newstring(sp)
  716.      while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
  717.        {
  718.        ++ sp;
  719.        ++ osp;
  720.        ++ othersp;
  721.        newstring(sp)
  722.        }
  723.      if ( sp == ep )
  724.        break;
  725.  
  726.      newstring(ep);
  727.      while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
  728.        {
  729.        -- ep;
  730.        -- oep;
  731.        -- otherep;
  732.        newstring(ep)
  733.        }
  734.      if ( sp == ep )
  735.        break;
  736.  
  737.      t   = *sp;
  738.      *sp = *ep;
  739.      *ep = t;
  740.  
  741.      t      = *othersp;
  742.      *othersp = *otherep;
  743.      *otherep = t;
  744.  
  745.      u    = *osp;
  746.      *osp = *oep;
  747.      *oep = u;
  748.      }
  749.  
  750.  t = *sp;
  751.  if ((curstrlen < off) || ((t[off] & m) == 0))
  752.     {
  753.     if ( ep != end )
  754.        {
  755.        ++ ep;
  756.        ++ oep;
  757.        ++ otherep;
  758.        }
  759.     }
  760.  else
  761.     {
  762.     if ( sp != start )
  763.        {
  764.        -- sp;
  765.        -- osp;
  766.        -- othersp;
  767.        }
  768.     }
  769.  
  770.  m >>= 1;
  771.  if ( m == 0 )
  772.     {
  773.     m = 0x80;
  774.     if ( ++off >= maxstrlen )  /*  Finished sorting block of strings:    */
  775.        {                               /*  Restore duplicates to
  776. riginal order  */
  777.        reg Byte **cp;
  778.        reg int *ocp;
  779.          Byte **othercp;
  780.  
  781.  
  782.        if ( sp != start )
  783.         {
  784.         cp  = start;
  785.         ocp = ostart;
  786.         othercp = otherstart;
  787.         while ( cp != sp )
  788.            {
  789.            if ( *ocp > *(ocp+1) )
  790.                {
  791.                t       = *(cp+1);
  792.                *(cp+1) = *cp;
  793.                *cp     = t;
  794.  
  795.                t               = *(othercp+1);
  796.                *(othercp+1)    = *othercp;
  797.                *othercp        = t;
  798.  
  799.                u        = *(ocp+1);
  800.                *(ocp+1) = *ocp;
  801.                *ocp     = u;
  802.  
  803.                if ( cp != start )
  804.                   {
  805.                   -- cp;
  806.                   -- ocp;
  807.                   -- othercp;
  808.                   continue;
  809.                   }
  810.                }
  811.            ++ cp;
  812.            ++ ocp;
  813.            ++ othercp;
  814.            }
  815.         }
  816.        if ( ep != end )
  817.         {
  818.         cp  = ep;
  819.         ocp = oep;
  820.         othercp = otherep;
  821.         while ( cp != end )
  822.            {
  823.            if ( *ocp > *(ocp+1) )
  824.                {
  825.                t       = *(cp+1);
  826.                *(cp+1) = *cp;
  827.                *cp     = t;
  828.  
  829.                t               = *(othercp+1);
  830.                *(othercp+1)    = *othercp;
  831.                *othercp        = t;
  832.  
  833.                u        = *(ocp+1);
  834.                *(ocp+1) = *ocp;
  835.                *ocp     = u;
  836.  
  837.                if ( cp != ep )
  838.                   {
  839.                   -- cp;
  840.                   -- ocp;
  841.                   -- othercp;
  842.                   continue;
  843.                   }
  844.                }
  845.            ++ cp;
  846.            ++ ocp;
  847.            ++ othercp;
  848.            }
  849.         }
  850.        return;
  851.        }
  852.     }
  853.  
  854.  
  855.  if ( sp != start )
  856.     sortstrs_block_oo ( start, sp, off, m, ostart, osp, otherstart, othersp );
  857.  if ( ep != end )
  858.     sortstrs_block_oo ( ep, end, off, m, oep, oend, otherep, otherend );
  859.  }
  860.  
  861.  
  862. /*    Function Name: InitManual
  863.  *    Description: Initializes this manual section.
  864.  *    Arguments: l_manual - local copy of the manual structure.
  865.  *                 label - the button label for this section.
  866.  *    Returns: none.
  867.  */
  868.  
  869. static void
  870. InitManual(l_manual, label)
  871. Manual * l_manual;
  872. char * label;
  873. {
  874.   bzero( l_manual, sizeof(Manual) );            /* clear it. */
  875.   l_manual->blabel = label;                    /* set label. */
  876. }
  877.   
  878. #if defined(DEBUG)
  879.  
  880. /*    Function Name: DumpManual
  881.  *    Description: Debugging function that dumps the entire manual page
  882.  *                   structure.
  883.  *    Arguments: number - the number of sections.
  884.  *    Returns: none.
  885.  */
  886.  
  887. DumpManual(number)
  888. {
  889.   register int i,j;
  890.   
  891.   for ( i = 0; i < number; i++) {
  892.     printf("label: %s\n", manual[i].blabel);
  893.     for (j = 0; j < manual[i].nentries; j++) 
  894.       printf("%s\n", manual[i].entries[j]);
  895.   }
  896. }
  897.  
  898. #endif /* DEBUG */
  899.